//################################################################################
// MIT License
// Copyright (c) 2024 ZhangYihua
//
// Change Logs:
// Date           Author       Notes
// 2023-09-01     ZhangYihua   first version
//
// Description  : 
//################################################################################

module s_dat_mul_coe #(
parameter           DW                      = 12,
parameter           CW                      = 16,       // CW>=2
parameter           NO_CIN_NMAX             = 1'b1,     // 1'b0:-1.0<=cin<1.0; 1'b1:-1.0<cin<1.0 (cin!=-1.0)
parameter           TRU_MODE                = "CBB_DEFINE"  // default truncation mode follows cbb_define.v 
//parameter           TRU_MODE                = "FLOOR"   // discade fractional bits directly for less area and higher Fmax
//parameter           TRU_MODE                = "ROUND"   // discade or carry according to MSB of fractonal bits for better DC
) ( 
input                                       rst_n,
input                                       clk,
input                                       cke,

input       signed  [DW-1:0]                din,
input       signed  [CW-1:0]                cin,        // s(CW, CW-1)

output      signed  [DW-1:0]                dout
);

//################################################################################
// define local varialbe and localparam
//################################################################################
localparam          PW                      = DW+CW;

reg         signed  [PW-1:0]                p;

//################################################################################
// main
//################################################################################

always@(posedge clk or negedge rst_n) begin
    if (rst_n==1'b0) begin
        p <=`U_DLY {PW{1'b0}};
    end else if (cke==1'b1) begin
        p <=`U_DLY din*cin;
    end
end

generate if (NO_CIN_NMAX==1'b1) begin:G_NMAX
    // becase  -2^(DW-1)    <= din <= 2^(DW-1) -1
    //         -2^(CW-1) +1 <= cin <= 2^(CW-1) -1
    //
    // so      -2^(DW+CW-2) < MIN(-2^(DW+CW-2)+2^(DW-1), -2^(DW+CW-2)+2^(CW-1)+2^(DW-1)-1) <= p
    //         p <= MAX(2^(DW+CW-2)-2^(DW-1), 2^(DW+CW-2)-2^(CW-1)-2^(DW-1)+1)< 2^(DW+CW-2)
    //
    // so      -2^(PW-1-1) < p <= 2^(PW-1-1) -1
//    assign dout = p[PW-2-:DW];
    s_sat_tru #(     // range [-(2^(IDW-1))/(2^IFW):(2^(IDW-1)-1)/(2^IFW)]
        .IDW                            (PW-1                           ),	// input data width, (IDW-IFW)>=(ODW-OFW)
        .IFW                            (CW-1                           ),	// input fractional width,  IFW>=IDW is legal
        .ODW                            (DW                             ),	// output data width
        .OFW                            (0                              ),	// output fractional width, OFW>=ODW is legal
        .TRU_MODE                       (TRU_MODE                       )
    ) u_p_st ( 
        .id                             (p[PW-2:0]                      ),	// s(IDW, IFW), the MSB is sign
        .od                             (dout                           ),	// s(ODW, OFW), the MSB is sign
        .over                           (                               )
    );

end else begin:G_NORM
    
    s_sat_tru #(     // range [-(2^(IDW-1))/(2^IFW):(2^(IDW-1)-1)/(2^IFW)]
        .IDW                            (PW                             ),	// input data width, (IDW-IFW)>=(ODW-OFW)
        .IFW                            (CW-1                           ),	// input fractional width,  IFW>=IDW is legal
        .ODW                            (DW                             ),	// output data width
        .OFW                            (0                              ),	// output fractional width, OFW>=ODW is legal
        .TRU_MODE                       (TRU_MODE                       )
    ) u_p_st ( 
        .id                             (p                              ),	// s(IDW, IFW), the MSB is sign
        .od                             (dout                           ),	// s(ODW, OFW), the MSB is sign
        .over                           (                               )
    );

end endgenerate

//################################################################################
// ASSERTION
//################################################################################

`ifdef CBB_ASSERT_ON
// synopsys translate_off

a_cin_nmax: assert property(@(posedge clk) disable iff (!rst_n)
    (cke&(NO_CIN_NMAX==1'b1)) |-> (cin!={1'b1, {CW-1{1'b0}}})
) else begin
    $error("[ASSERT FAILED] ");
end

// synopsys translate_on
`endif

endmodule
